﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using AsmDependency.Utility;

namespace AsmDependency
{
    internal class DependencyAnalizerAsyncTasks : CAsyncTask
    {
        #region [=== VARIABLES ===]
        bool _processing = false;
        string _DomainDirectory = string.Empty;
        string _componentPath = string.Empty;
        string _componentConfigPath = string.Empty;
        string _targetComponentDirectory = string.Empty;

        DependencyAnalizerAsyncTasks _dependencyAnalizer;

        AppDomainSetup _domainInformation;
        AppDomain _applicationDomain;
        ApplicationWindow _owner;
        AssemblyName[] _references;
        #endregion

        public DependencyAnalizerAsyncTasks(ApplicationWindow owner, string domainDorectoy, string componentPath, string componentConfigPath) {
            this._owner = owner;
            this._DomainDirectory = domainDorectoy;
            this._componentPath = componentPath;
            this._componentConfigPath = componentConfigPath;
        }

        #region [=== METHODS ===]
        internal string GetFromResources(string resourceName)
        {
            Assembly assem = Assembly.GetExecutingAssembly();
            using (Stream stream = assem.GetManifestResourceStream(resourceName))
            {
                using (var reader = new StreamReader(stream))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        internal AssemblyName[] GetReferences(string _asm)
        {
            // Determinate reference list
            Assembly asm = Assembly.LoadFile(_asm);
            List<AssemblyName> _AssemblyNameList = new List<AssemblyName>();
            foreach (AssemblyName item in asm.GetReferencedAssemblies())
            {
                if (!GetFromResources("AsmDependency.BCL.txt").Contains(item.Name)) {
                    Console.WriteLine(item.Name);
                    Console.WriteLine(Assembly.GetAssembly(item.GetType()).CodeBase);
                    _AssemblyNameList.Add(item);
                }

            }

            foreach (AssemblyName item in _AssemblyNameList)
            {
                Assembly _Assembly;
                string _comp = Path.Combine(_targetComponentDirectory, item.Name + ".dll");
                if (File.Exists(Path.Combine(_DomainDirectory, item.Name + ".dll"))) {
                    _Assembly = Assembly.LoadFile(Path.Combine(_DomainDirectory, item.Name + ".dll"));
                    File.Copy(Path.Combine(_DomainDirectory, item.Name + ".dll"), Path.Combine(_targetComponentDirectory, item.Name + ".dll"), true);

                } else {
                    _Assembly = Assembly.Load(item);
                    File.Copy(_Assembly.Location, Path.Combine(_targetComponentDirectory, item.Name + ".dll"), true);
                }

                Console.WriteLine(_Assembly.Location);
                AssemblyName[] _list = GetReferences(_Assembly.Location);
                if(_list.Length > 0) _AssemblyNameList.AddRange(_list);
            }

            return _AssemblyNameList.ToArray();
        }
        #endregion

        #region [=== OVERRIDE ===]
        protected override void PerformTask()
        {
            bool flag = false;
            try
            {
                _domainInformation = new AppDomainSetup();
                _domainInformation.ApplicationBase = _DomainDirectory;
                _domainInformation.ConfigurationFile = _componentConfigPath;
                _domainInformation.DisallowBindingRedirects = false;
                _domainInformation.DisallowCodeDownload = true;

                _applicationDomain = AppDomain.CreateDomain("MyDomain", null, _domainInformation);
                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ApplicationDomain_AssemblyResolve);

                _references = GetReferences(_componentPath);
                AppDomain.Unload(_applicationDomain);
            } catch {
                flag = true;
            }
            finally
            {
                //this._owner.Processing = false;
            }
            if (!flag)
            {
                base.PostResults(_references, 100, true);
            }
            else
            {
                base.PostResults(null, 100, true);
            }
        }
        #endregion

        #region [=== EVENTS HANDLERS ===]
        internal Assembly ApplicationDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            //This handler is called only when the common language runtime tries to bind to the assembly and fails.

            //Retrieve the list of referenced assemblies in an array of AssemblyName.
            Assembly MyAssembly, objExecutingAssemblies;
            string strTempAssmbPath = "";

            objExecutingAssemblies = Assembly.GetExecutingAssembly();
            AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();

            //Loop through the array of referenced assembly names.
            foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
            {
                //Check for the assembly names that have raised the "AssemblyResolve" event.
                if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
                {
                    //Build the path of the assembly from where it has to be loaded.				
                    strTempAssmbPath = _DomainDirectory + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
                    break;
                }

            }
            //Load the assembly from the specified path. 					
            MyAssembly = Assembly.LoadFrom(strTempAssmbPath);

            //Return the loaded assembly.
            return MyAssembly;
        }
        #endregion
    }
}
